package xyz.zcq.zcqrpc.spi;

import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * SPI加载器（支持键值对映射）
 */
@Slf4j
public class SpiLoader {

    /**
     * 系统 SPI 目录
     */
    private static final String RPC_SYSTEM_SPI_DIR = "META-INF/rpc/system/";
    /**
     * 用户自定义 SPI 目录
     */
    private static final String RPC_CUSTOM_SPI_DIR = "META-INF/rpc/custom/";
    /**
     * 扫描路径
     */
    private static final String[] SCAN_DIRS = new String[]{RPC_SYSTEM_SPI_DIR, RPC_CUSTOM_SPI_DIR};
    /**
     * 存储已加载的类
     * key：接口类名
     * value：实现类key -> 实现类Class对象
     */
    private static final Map<String, Map<String, Class<?>>> loaderMap = new ConcurrentHashMap<>();
    /**
     * 类实例缓存，避免重复创建实例
     * key：类路径
     * value：实例
     */
    private static final Map<String, Object> instanceCache = new ConcurrentHashMap<>();

    /**
     * 加载指定类型
     *
     * @param clazz 指定类型的Class对象
     */
    public static void load(Class<?> clazz) {
        String interfaceName = clazz.getName();
        log.info("加载类型为 {} 的 SPI", interfaceName);

        Map<String, Class<?>> keyClassMap = new HashMap<>();
        for (String scanDir : SCAN_DIRS) {
            List<URL> resources = ResourceUtil.getResources(scanDir + interfaceName);
            for (URL resource : resources) {
                try (InputStream inputStream = resource.openStream();
                     InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
                     BufferedReader bufferedReader = new BufferedReader(inputStreamReader)
                ) {
                    String line;
                    while (StrUtil.isNotBlank(line = bufferedReader.readLine())) {
                        String[] split = line.split("=");
                        keyClassMap.put(split[0], Class.forName(split[1]));
                    }
                } catch (Exception e) {
                    log.error("spi resource load error", e);
                }
            }
        }

        loaderMap.put(interfaceName, keyClassMap);
    }

    /**
     * 获取指定接口的实例
     *
     * @param clazz 接口的Class对象
     * @param key   实例的key
     * @param <T>   接口的类型
     * @return 实例
     */
    public static <T> T getInstance(Class<T> clazz, String key) {
        String className = clazz.getName();

        Map<String, Class<?>> keyClassMap = loaderMap.get(className);
        if (keyClassMap == null) {
            throw new RuntimeException(String.format("SpiLoader 未加载 %s 类型", className));
        }
        if (!keyClassMap.containsKey(key)) {
            throw new RuntimeException(String.format("SpiLoader 的 %s 不存在 key=%s 的类型", className, key));
        }
        Class<?> implClazz = keyClassMap.get(key);
        String implName = implClazz.getName();
        if (!instanceCache.containsKey(implName)) {
            try {
                instanceCache.put(implName, implClazz.newInstance());
            } catch (InstantiationException | IllegalAccessException e) {
                String errorMsg = String.format("%s 类实例化失败", implName);
                throw new RuntimeException(errorMsg, e);
            }
        }

        return clazz.cast(instanceCache.get(implName));
    }

}
